// TCPClient project main.go
package main

import (
	"bufio"
	"echoClient/setting"
	"encoding/hex"
	"flag"
	"fmt"
	"io"
	"net"
	"os"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
	"time"
)

var wg sync.WaitGroup
var sendBytes []byte
var chan_resp chan string
var chan_err chan string
var service string
var tcpAddr *net.TCPAddr
var loop int
var flowNum int
var recvMax int
var errsum uint32 = 0
var MsgToSvr [100000][]byte
var fileSetting = flag.String("setting", "conf/settings.json", "file path name of settings")

func tcp_handle(idx int, round int) {

	var buf [1024]byte
	var conn *net.TCPConn
	var err error
	var erronce bool = false
	var s string
	s = fmt.Sprintf("%d-%d-%d:", idx, round, loop)
	conn, err = net.DialTCP("tcp", nil, tcpAddr)
	if err != nil {
		erronce = true
		s = s + "fail to connect to server." + err.Error()
	} else {
		defer conn.Close()
		//fmt.Println(idx, round, loop, "SENDING:", string(hex.EncodeToString(MsgToSvr[idx])))
		conn.SetReadDeadline(time.Now().Add(time.Second * 3))
		n, err := conn.Write(MsgToSvr[idx%46000])
		if err != nil {
			erronce = true
			s = s + "fail to send data to server." + err.Error()
		} else {
			conn.SetReadDeadline(time.Now().Add(time.Second * 3))
			n, err = conn.Read(buf[0:])
			if err != nil {
				erronce = true
				s = s + "fail to read data1 from server." + err.Error()
			} else {
				if n < recvMax {
					s = s + ("Reply from server " + hex.EncodeToString(buf[0:n]))
					conn.SetReadDeadline(time.Now().Add(time.Second * 5))
					n, err = conn.Read(buf[0:])
					if err != nil {
						erronce = true
						s = s + "fail to read data2 from server." + err.Error()
					} else {
						s = s + hex.EncodeToString(buf[0:n])
					}
				}
			}
		}
	}

	if round < loop {
		go tcp_handle(idx+flowNum, round+1)
	}
	if erronce {
		chan_err <- s
		atomic.AddUint32(&errsum, 1)
	} else {
		chan_resp <- s
	}
	wg.Done()

}

// 初始化配置
func InitConf() {
	err := settings.Init(*fileSetting)
	if err != nil {
		fmt.Println("fail to read config file ", *fileSetting, err.Error())
		return
	}
}

// 按行读取文件
func ReadLine(fileName string, handler func(string, int)) error {
	f, err := os.Open(fileName)
	checkErr(err)
	buf := bufio.NewReader(f)
	i := 0
	for {
		line, err := buf.ReadString('\n')
		line = strings.TrimSpace(line)
		handler(line, i)
		i += 1
		if err != nil {
			if err == io.EOF {
				return nil
			}
			checkErr(err)
		}
	}
	return nil
}

func main() {
	InitConf()
	runtime.GOMAXPROCS(1)
	var err error
	flowNum, err = strconv.Atoi(settings.GetNum())
	checkErr(err)
	recvMax, err = strconv.Atoi(settings.GetRecvMax())
	checkErr(err)
	loop, err = strconv.Atoi(settings.GetLoop())
	checkErr(err)
	service = settings.GetIp() + ":" + settings.GetTcpPort()
	tcpAddr, err = net.ResolveTCPAddr("tcp4", service)
	checkErr(err)
	chan_resp = make(chan string, flowNum*loop)
	chan_err = make(chan string, flowNum*loop)
	ReadLine("login.csv", func(s string, i int) {
		MsgToSvr[i], _ = hex.DecodeString(s)
	})

	start := time.Now().UnixNano()
	for i := 0; i < flowNum; i++ {
		wg.Add(loop)
		go tcp_handle(i, 1)
	}
	wg.Wait()
	lapse := time.Now().UnixNano() - start
	close(chan_resp)
	close(chan_err)
	count := 0
	for v := range chan_resp {
		count = count + 1
		fmt.Println(count, v)
	}
	for v := range chan_err {
		count = count + 1
		fmt.Println(count, v)
	}
	fmt.Println(flowNum*loop, "连接，总耗时", lapse, "纳秒, OR", lapse/1000000, " 毫秒, errnum : ", errsum)
	fmt.Println("Press 'Enter' to continue...")
	bufio.NewReader(os.Stdin).ReadBytes('\n')
}

func checkErr(err error) {
	if err != nil {
		fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())
		//os.Exit(1)
	}
}
